//
// Created by lz on 3/1/21.
//

#include "net/poller/EpollPoller.h"

#include "base/Logging.h"
#include "net/Channel.h"

#include <cassert>
#include <cerrno>
#include <poll.h>
#include <sys/epoll.h>
#include <unistd.h>

using namespace lzweb;
using namespace lzweb::net;

// On Linux, the constants of poll(2) and epoll(4)
// are expected to be the same.

static_assert(EPOLLIN == POLLIN,        "epoll uses same flag values as poll");
static_assert(EPOLLPRI == POLLPRI,      "epoll uses same flag values as poll");
static_assert(EPOLLOUT == POLLOUT,      "epoll uses same flag values as poll");
static_assert(EPOLLRDHUP == POLLRDHUP,  "epoll uses same flag values as poll");
static_assert(EPOLLERR == POLLERR,      "epoll uses same flag values as poll");
static_assert(EPOLLHUP == POLLHUP,      "epoll uses same flag values as poll");

namespace
{
	const int kNew = -1;
	const int kAdded = 1;
	const int kDeleted = 2;
}

EpollPoller::EpollPoller(EventLoop* loop)
: Poller(loop),
epollfd_(::epoll_create(EPOLL_CLOEXEC)),
events_(kInitEventListSize)
{
	if (epollfd_ < 0)
	{
		LOG_SYSFATAL << "EPollPoller::EPollPoller";
	}
}

EpollPoller::~EpollPoller()
{
	::close(epollfd_);
}

//poll 后通过参数返回一个当前活跃的activeChannelList
TimeStamp EpollPoller::poll(int timeoutMs, Poller::ChannelList* activeChannel)
{
	LOG_TRACE << "fd total count " << channels_.size();

	//maxevents是最大的返回关注事件数，so events是一个专门用来存储返回事件的数组，其初始时不包含内容（不像poll）
	int numEvents = ::epoll_wait(epollfd_, &*events_.begin(),
		static_cast<int>(events_.size()), timeoutMs);
	int savedErrno = errno;

	TimeStamp now(TimeStamp::now());
	//有事件发生
	if (numEvents > 0)
	{
		LOG_TRACE << numEvents << " events happened";
		fillActiveChannels(numEvents,activeChannel);
		if (implicit_cast<size_t>(numEvents) == events_.size())
		{
			events_.resize(events_.size() * 2);
		}
	}
	else if ( numEvents == 0)
	{
		LOG_TRACE << "nothing happened";
	}
	else
	{
		//error happens, log uncommon ones
		if(savedErrno != EINTR)
		{
			errno = savedErrno;
			LOG_SYSERR << " EPollPoller::poll()";
		}
	}
	return now;
}

void EpollPoller::fillActiveChannels(int numEvents, Poller::ChannelList* activeChannels) const
{
	assert(implicit_cast<size_t>(numEvents) <= events_.size());
	for (int i = 0; i < numEvents; ++i)
	{
		Channel* channel = static_cast<Channel*>(events_[i].data.ptr);

#ifndef NDEBUG
		int fd = channel->fd();
		ChannelMap::const_iterator it = channels_.find(fd);
		assert(it != channels_.end());
		assert(it->second == channel);
#endif
		channel->set_revents(events_[i].events);
		activeChannels->push_back(channel);
	}
}

void EpollPoller::updateChannel(Channel* channel)
{
	Poller::assertInLoopThread();
	const int index = channel->index();
	LOG_TRACE << "fd = " << channel->fd()
		<< " events = "<< channel->events() << " index = " << index;

	//new / delete 都对应于在epoll增加一个fd
	if(index == kNew || index == kDeleted)
	{
		// a new one, add with EPOLL_CTL_ADD
		int fd = channel->fd();
		if (index == kNew)
		{
			assert(channels_.find(fd) == channels_.end());
			channels_[fd] = channel;
		}
		else // index == kDeleted
		{
			assert(channels_.find(fd) != channels_.end());
			assert(channels_[fd] == channel);
		}

		channel->set_index(kAdded);
		update(EPOLL_CTL_ADD, channel);
	}
	else
	{
		// update existing one with EPOLL_CTL_MOD/DEL
		int fd = channel->fd();
		(void)fd;
		assert(channels_.find(fd) != channels_.end());
		assert(channels_[fd] == channel);
		assert(index == kAdded);
		//通过channel感知其fd的关注事件的变化
		if(channel->isNoneEvent())
		{
			update(EPOLL_CTL_DEL, channel);
			channel->set_index(kDeleted);
		}
		else
		{
			update(EPOLL_CTL_MOD, channel);
		}
	}
}

void EpollPoller::removeChannel(Channel* channel)
{
	Poller::assertInLoopThread();
	int fd = channel->fd();
	LOG_TRACE << "fd = " << fd;
	assert(channels_.find(fd) != channels_.end());
	assert(channels_[fd] == channel);
	assert(channel->isNoneEvent());

	int index = channel->index();

	assert(index == kAdded || index == kDeleted);
	size_t n = channels_.erase(fd);
	(void) n;
	assert(n == 1);

	if (index == kAdded)
	{
		update(EPOLL_CTL_DEL, channel);
	}
	channel->set_index(kNew);
}

void EpollPoller::update(int operation, Channel* channel)
{
	struct epoll_event event;
	memZero(&event, sizeof event);
	event.events = channel->events();
	event.data.ptr = channel;
	int fd = channel->fd();
	LOG_TRACE << "epoll_ctl op = " << operationToString(operation)
			  << " fd = " << fd << " event = { " << channel->eventsToString() << " }";
	if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
	{
		if (operation == EPOLL_CTL_DEL)
		{
			LOG_SYSERR << "epoll_ctl op = " << operationToString(operation) << " fd =" << fd;
		}
		else
		{
			LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
		}
	}
}

const char* EpollPoller::operationToString(int op)
{
	switch (op)
	{
	case  EPOLL_CTL_ADD:
		return "ADD";
	case EPOLL_CTL_DEL:
		return "DEL";
	case EPOLL_CTL_MOD:
		return "MOD";
	default:
		//这里是在debug的时候直接退出？
		assert(false && "ERROR op");
		return "Unknown Operation";
	}
}





